#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/core/handle_policy.h>
#include <vex/core/as_any.h>
#include <vex/core/as_intrusive.h>
#include <vex/core/make_raw_handle.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_policy.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_properties.h>
#include <boost/utility/identity_type.hpp>

namespace test_ns { 
    namespace test_lib { namespace contract {
        struct VEX_ABSTRACT abstract_contract : public virtual vex::core::contract::handle {
            virtual void do_something() = 0;
            VEX_PROTECT_CONTRACT(abstract_contract);
        };

        template<class T> struct VEX_ABSTRACT abstract_t_contract : public virtual vex::core::contract::handle {
            virtual T get_value() const = 0;
            VEX_PROTECT_CONTRACT(abstract_t_contract);
        };
    }}

    using test_lib::contract::abstract_contract;
    using test_lib::contract::abstract_t_contract;

    namespace test_lib { namespace implementation {
        class VEX_ABSTRACT contract_impl : public contract::abstract_contract {
        protected:
            contract_impl() {}
            ~contract_impl() {}
        public:
            virtual void do_something() {}
        };

        template<class T> class VEX_ABSTRACT t_contract_impl : public contract::abstract_t_contract<T> {
            T m_value;
        protected:
            t_contract_impl(T p_value) : m_value(p_value) {}
            ~t_contract_impl() {}
        public:
            virtual T get_value() const
            {
                return m_value;
            }
        };
    }}

    namespace test_lib {

        struct any_traits {
            typedef contract::abstract_contract contract_type;
            typedef boost::intrusive_ptr<contract_type> holder_type;
            typedef contract_type* item_type;
        };

        class any : public vex::core::handle_policy<
            any,
            any_traits
        >
        {
        public:
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any, 
                any_traits, 
                VEX_TRAITS_IS_NOT_DEPENDENT_NAME);
        private:
            holder_type m_holder;
        };

        /// adl as_any support, must be in contract's namespace
        namespace contract {
            any adl_as_any_from_out(abstract_contract* p_handle)
            {
                return any(p_handle, false);
            }

            any adl_as_any_from_in(abstract_contract* p_handle)
            {
                return any(p_handle, true);
            }
        }

        template<class T>
        struct any_t_traits {
            typedef contract::abstract_t_contract<T> contract_type;
            typedef boost::intrusive_ptr<contract_type> holder_type;
            typedef contract_type* item_type;
        };

        template<class T>
        class any_t : public vex::core::handle_policy<
            any_t<T>,
            any_t_traits<T>
        >
        {
        public:
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any_t, 
                any_t_traits<T>, 
                VEX_TRAITS_IS_DEPENDENT_NAME);
        private:
            holder_type m_holder;
        };

        /// adl as_any support, must be in contract's namespace
        namespace contract {
            template<class T> any_t<T> adl_as_any_from_out(abstract_t_contract<T>* p_handle)
            {
                return any_t<T>(p_handle, false);
            }

            template<class T> any_t<T> adl_as_any_from_in(abstract_t_contract<T>* p_handle)
            {
                return any_t<T>(p_handle, true);
            }
        }
    }

    using test_lib::any;
    using test_lib::any_t;
}

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

//namespace vex {
//    // visible to contracts
//    template<class THandle> class input_parameter_base {
//        input_parameter_base(input_parameter_base const & p_other);
//        input_parameter_base& operator=(input_parameter_base const & p_other);
//    protected:
//        THandle* m_handle;
//
//        template<class UHandle>
//        input_parameter_base(UHandle*& p_handle) : m_handle(p_handle) {}
//
//        input_parameter_base(input_parameter_base&& p_other) 
//            : m_handle(p_other.m_handle) 
//        {
//            p_other.m_handle = nullptr;
//        }
//
//        ~input_parameter_base() {}
//    public:
//        THandle* operator -> () const
//        {
//            return m_handle;
//        }
//
//        THandle* get() const
//        {
//            return m_handle;
//        }
//    };
//
//    // visible to implementations
//    template<class THandle> class input_parameter : public input_parameter_base<THandle> {
//    public:
//        template<class UHandle>
//        input_parameter(UHandle*& p_handle, typename std::enable_if<
//            std::is_convertible<UHandle*, THandle*>::value>::type* = 0) 
//            : input_parameter_base<THandle>(p_handle) {}
//
//        input_parameter(input_parameter&& p_other)
//            : input_parameter_base(p_other) {}
//
//        void swap(input_parameter& p_other)
//        {
//            return std::swap(this->m_handle, p_other.m_handle);
//        }
//
//        input_parameter& operator=(input_parameter&& p_other)
//        {
//            input_parameter t_tmp(p_other);
//            this->swap(t_tmp);
//            return *this;
//        }
//    };
//
//    template<class THandle> input_parameter<THandle> as_input_parameter(THandle*& p_handle)
//    {
//        return input_parameter<THandle>(p_handle);
//    }
//
//    template<class TSmartPtr> auto as_input_parameter(TSmartPtr& p_smart_ptr)
//        -> input_parameter<typename TSmartPtr::element_type>
//    {
//        auto t_lvalue = p_smart_ptr.get();
//        return input_parameter<typename TSmartPtr::element_type>(t_lvalue);
//    }
//}

namespace tests { namespace core_adl_as_any {

    TEST_CLASS(test_class)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.core.adl_as_any")
        END_TEST_CLASS_ATTRIBUTE();

        VEX_TESTS_TRACKED_MEM_CHECK();
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_START();
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
        }

        //void take_as_input(vex::input_parameter_base<test_ns::abstract_contract>& p_input)
        //{
        //    auto t_owner = vex::as_any_from_input() | p_input.get();
        //    t_owner->do_something();
        //}

        //TEST_METHOD(pass_temporary_as_input_parameter)
        //{
        //    auto t_stable = vex::as_any_from_output() |
        //        vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>();
        //    auto t_stable_input = vex::as_input_parameter(t_stable);
        //    this->take_as_input(t_stable_input);
        //}

        TEST_METHOD(adl_as_any_from_out_for_any_t_is_found_and_applied_correctly)
        {
            {
                auto h = adl_as_any_from_out(vex::core::make_raw_handle<
                    test_ns::test_lib::implementation::t_contract_impl<int>
                >(1));
                Assert::IsTrue(h);
                Assert::AreEqual(1, h->get_value());
            }
        }

        TEST_METHOD(adl_as_any_from_out_for_any_is_found_and_applied_correctly)
        {
            {
                auto h = adl_as_any_from_out(vex::core::make_raw_handle<
                    test_ns::test_lib::implementation::contract_impl
                >());
                Assert::IsTrue(h);
                h->do_something();
            }

            {
                auto h = adl_as_any_from_out(static_cast<test_ns::abstract_contract*>(
                    vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>()));
                Assert::IsTrue(h);
                h->do_something();
            }
        }

        TEST_METHOD(adl_as_any_from_in_for_any_is_found_and_applied_correctly)
        {
            auto h = adl_as_any_from_out(
                vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>());

            {
                auto p_in = adl_as_any_from_in(vex::as_raw_in(h));
            }
        }

        TEST_METHOD(as_any_from_output_for_any_is_found_and_applied_correctly)
        {
            auto h = vex::as_any_from_output()
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>();
            Assert::IsTrue(h);
            h->do_something();
        }

        TEST_METHOD(as_any_from_input_for_any_is_found_and_applied_correctly)
        {
            auto h = vex::as_any_from_output()
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>();
            Assert::IsTrue(h);

            {
                auto h_in = vex::as_any_from_input()
                    | vex::as_raw_in(h);
                Assert::IsTrue(h_in);
                h_in->do_something();
            }
        }

        TEST_METHOD(as_any_from_out_from_any_is_found_and_applied_correctly)
        {
            auto h = vex::as_any_from_out(
                vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>());
            Assert::IsTrue(h);
            h->do_something();
        }

        TEST_METHOD(any_correctly_implements_handle_policies)
        {
            auto h1 = vex::as_any_from_output() 
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>();
            Assert::IsTrue(h1);

            auto h2 = vex::as_any_from_output() 
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::contract_impl>();
            Assert::IsTrue(h2);

            handle_policy::correctly_implements_handle_policy(h1, h2);
        }

        TEST_METHOD(any_t_correctly_implements_handle_policies)
        {
            auto h1 = vex::as_any_from_output() 
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::t_contract_impl<int>>(1);
            Assert::IsTrue(h1);

            auto h2 = vex::as_any_from_output() 
                | vex::core::make_raw_handle<test_ns::test_lib::implementation::t_contract_impl<int>>(2);
            Assert::IsTrue(h2);

            handle_policy::correctly_implements_handle_policy(h1, h2);
        }
    };
}}